# -*- coding: utf-8 -*-
'''
主要负责记录gcc命令, 保存依赖信息：依赖文件，编译命令，然后并执行对应的编译命令
'''
import sys
import os
import copy
import logging 
import simplejson

sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir))

from command_parser.gcc_parse.gcc_command_parser import GCCParser
from utils.common_utils import *
from configure.compiler_configure import *
from base.context import Context
from utils.db_object_pool import DBObjectPool
from configure.env_config import EnvConfig
from configure.config_logging import capture_log_init

class GCCHandler(object):
    def __init__(self, command=[]):
        self.command = command
        self.env = {}
        self.cwd = ""
        self.context = Context()
        self.capture_db_handler = None
        self.cache_db_handler = None
    
    def init_env(self):
        self.cwd = os.getcwd()
        self.env = dict(os.environ)
        remove_envs_by_keys(self.env, ["LD_PRELOAD"])
        self.context.update_work_path(os.path.dirname(self.env["IRGEN_WORK_PATH"]))
        self.init_logger()
        self.context.check_redis_cached_config()
        self.capture_db_handler = DBObjectPool.get_db_inst_by_name("gcc")
    
    def init_logger(self):
        capture_log_init(log_dir=self.context.LOG_DIR, log_mode="a", log_name="gcc.log", log_level="DEBUG")
    
    def run_gcc_handler(self):
        
        logging.debug("Original GCC Command: %s" % " ".join(self.command))
        parser = GCCParser(context=self.context, command=self.command)
        compiler, options = parser.parse_command()
        action_type = parser.get_action_type()
        
        if action_type == PREPROCESS:
            command_line = parser.get_original_command()
            ret = run_command(command=command_line, env=self.env)
            # print("[INFO] Catch a preprocess command: %s" % ' '.join(command_line))
            if ret:
                logging.warning("gcc command failed: %s, Error Code(%d)" % (' '.join(command_line), ret))
            else:
                logging.debug("gcc preprocess command: %s" % (' '.join(command_line)))
            return ret
        
        elif action_type == COMPILE:
            inputs = parser.get_input_options()
            inputs_len = len(inputs)
            for pop_index in range(inputs_len):
                local_options = copy.deepcopy(options)
                local_parser = GCCParser(command="", context=self.context, compiler=compiler, options=local_options,
                                         cwd=os.getcwd())
                local_inputs = local_parser.get_input_options()
                local_inputs.pop(pop_index)
                local_parser.delete_options_by_list(remove_list=local_inputs)
                local_command = local_parser.get_divided_compile_command()
                # active_cache_env(env=self.env, context=self.context)
                ret = run_reassemble_command(command=local_command, env=self.env)
                if ret is not 0:
                    logging.warning("gcc command failed: %s, Error Code(%d)" % (' '.join(local_command), ret))
                    return ret
                else:
                    logging.debug("Record command: {}".format(' '.join(local_command)))
                    # print("[INFO] Capture GCC Command: %s" % ' '.join(local_command))
                self.record_command(parser=local_parser)
                
        elif action_type == COMPILE_LINK:
            
            inputs = parser.get_input_options()
            outputs = parser.get_output_options()
            inputs_len = len(inputs)
            parser.delete_options_by_list(remove_list=outputs)
            for pop_index in range(inputs_len):
                
                if not parser.check_is_src_file(inputs[pop_index]):
                    continue
                
                local_options = copy.deepcopy(options)
                local_parser = GCCParser(context=self.context, compiler=compiler, options=local_options, 
                                         cwd=os.getcwd())
                local_inputs = local_parser.get_input_options()
                local_inputs.pop(pop_index)
                local_parser.delete_options_by_list(local_inputs)
                local_command = local_parser.get_divided_compile_command()
                local_output = local_parser.get_output_options()
                inputs[pop_index].parameter = local_output[0].parameter
                # active_cache_env(env=self.env, context=self.context)
                ret = run_reassemble_command(command=local_command, env=self.env)
                if ret != 0:
                    logging.warning("gcc command failed: %s, Error Code(%d)" % (' '.join(command_line), ret))
                    return ret
                else:
                    logging.debug("Record command: {}".format(' '.join(local_command)))
                    # print("[INFO] Capture GCC Command: %s" % ' '.join(local_command))
                self.record_command(parser=local_parser)

            parser.extend_options_by_list(extend_list=outputs)
            command_line = parser.get_divided_link_command()
            active_capture_env(env=self.env, context=self.context)
            ret = run_reassemble_command(command=command_line, env=self.env)
        
        elif action_type == LINK:
            command_line = parser.get_original_command()
            # print("[GCC][Link] {}".format(" ".join(command_line)))
            logging.debug("gcc Link Command: %s" % ' '.join(command_line))
            active_capture_env(env=self.env, context=self.context)
            ret = run_command(command=command_line, env=self.env)
            
        return 0
    
    def record_command(self, parser=None):
        # logging.debug("Record command: %s", ' '.join(command))
        
        if parser is None:
            logging.error("command record failed: parser is not initial")
            return
        
        parser.resolve_symbolic_inputs()
        outputs = parser.get_output_options()
        inputs = parser.get_input_options()
        
        
        command = parser.get_original_command()
        if not outputs or not outputs[0].parameter:
            raise Exception("Command: %s without any output" % ' '.join(command))
            
        output = outputs[0].parameter[0]
        real_output = real_file_path(output, parser.cwd)
        dependencies = parser.parse_dep_files()
        # print(dependencies, parser.get_input_options()[0].parameter[0])
        self.save_dep_info(name=real_output, command=command, input_files=dependencies)
    
    def save_dep_info(self, name="", command=[], input_files=[]):
        output_md5 = file_md5(name)
        # if the output file is not exsit, then we filter this
        if (not output_md5):
            return 
        
        self.capture_db_handler.set_name_dep(name_md5=output_md5, name=name)
        dep_files_md5 = []
        for dep_file in input_files:
            dep_file = real_file_path(dep_file)
            dep_file_md5 = file_md5(dep_file)
            if dep_file_md5:
                dep_files_md5.append(dep_file_md5)
                self.capture_db_handler.set_name_dep(name_md5=dep_file_md5, name=dep_file)
        
        self.capture_db_handler.set_link_dep(name_md5=output_md5, links=dep_files_md5)
        self.capture_db_handler.set_cmd_dep(name_md5=output_md5, cmd=[self.cwd, command])

if __name__ == "__main__":
    command = sys.argv[2:]
    # print(command)
    handler =  GCCHandler(command)
    handler.init_env()
    handler.run_gcc_handler()
